-module(test_erlsom).
-export([test_xsd/1]).
-export([simple_test/0]).
-export([test_add/0]).
-export([test_xsd/2]).
-export([test_ms/1]).
-export([test_soap/1]).
-export([perf_test/2]).
-export([perf_test_xmerl/2]).
%% export because of timer:tc
-export([parse_n_times/3]).
-export([parse_n_times_xmerl/2]).
%% export beacause of 'all'
-export([execute_cases/2]).

-include("erlsom.hrl").
-record('testConfig', {elInfo, dir, cases}).
-record('case', {elInfo, name, path, desc, result, xsd, prefix, xml, namespaces}).
-record(namespace, {elInfo, uri, prefix}).
%% namespace rcord is defined in erlsom.hrl


debug(Text) ->
  io:format("~p\n", [Text]).

debugPrint(Text) ->
  debug("Print"),
  Out = io_lib:format("~p", [Text]),
  {ok, File} = file:open("model", [write]),
  Result = io:fwrite(File, "~p", [Text]),
  debug(Result).

simple_test() ->
  XsdFile = "c:/werk/test-xsd/test.xsd",
  {ok, Model} = erlsom:compile_file(XsdFile, []),
  debug(Model),
  Xml = "c:/werk/test-xsd/test.xml",
  {ok, Struct} = erlsom:parse_file(Xml, Model),
  debug(Struct).

test_add() ->
  XsdFile = "c:/werk/test-xsd/soap-envelope.xsd",
  Nss = [#ns{uri= "http://www.w3.org/2003/05/soap-envelope", prefix="sp"}],
  {ok, Model} = erlsom:compile_file(XsdFile, Nss),
  XsdFile2 = "c:/werk/test-xsd/tpp_auth.xsd",
  Nss2 = [#ns{uri="http://www.carus.de/projects/xaction/schemas/2001/08/requests/v60/stateful/XTC/TPP_AUTH", prefix="p"}],
  Model2 = erlsom:add_file(XsdFile2, Nss2, Model),
  Xml = "c:/werk/test-xsd/valid.xml",
  erlsom:parse_file(Xml, Model2).

test_xsd(Test) ->
  test_xsd(Test, []).

test_xsd(Test, Options) ->
  XsdFile = "testConfig.xsd",
  {ok, Model} = erlsom:compile_file(XsdFile),
  Xml = "testConfig.xml",
  {ok, TestSuite} = erlsom:parse_file(Xml, Model),
  debug("hier"),
  %% find test
  Cases = TestSuite#testConfig.cases,
  Dir = TestSuite#testConfig.dir,
  case Test of
    all ->
      lists:foldl({test_erlsom, execute_cases}, {Dir, Options}, Cases);
    _ ->
      case findCase(Cases, Test) of
        {ok, Case} -> execute_case(Case, Dir, Options);
        _Else -> debug("Case not found")
      end
  end.


test_ms(Test) ->
  XsdFile = "MStestsConfig.xsd",
  {ok, Model} = erlsom:compile_file(XsdFile),
  %% debug(Model),
  Xml = "testsMS_LTGfmt.xml",
  {ok, TestSuite} = erlsom:parse_file(Xml, Model).

execute_cases(Case, {Dir, Options}) ->
  execute_case(Case, Dir, Options),
  {Dir, Options}.

execute_case(#'case'{name=Name, path=Path, desc=Desc, result=Result, xsd=XSD, xml=XML, namespaces=NS, prefix=Pf},
             Dir, Options) ->
  Prefix = case Pf of undefined -> "P"; _ -> Pf end,
  io:format("test case: ~p - ~p\n", [Name, Desc]),
  erase(debug),
  CasePath = case Path of undefined -> Name; _ -> Path end,
  %% io:format("expected result: ~p\n", [Result]),
  XsdFile = filename:join([Dir, CasePath, XSD]),
  %% io:format("compiling xsd...\n"),
  Namespaces = translateNS(NS),
  CompilationResult = erlsom:compile_file(XsdFile, Prefix, Namespaces),
  case lists:member('model', Options) of true -> debug(CompilationResult); _ -> true end,
  case CompilationResult of
    {error, Message} ->
      case Result of
        "OK" -> 
          io:format("test case: ~p - ~p\n", [Name, Desc]),
          io:format("----> XSD error: ~p\n", [Message]);
        _ -> true
      end;
    {ok, Model} ->
      case lists:member('modelFile', Options) of true -> debugPrint(Model); _ -> true end,
      %% io:format("Model: ~p\n", [Model]),
      Xml = filename:join([Dir, CasePath, XML]),
      Input = case file:read_file(Xml) of
                {ok, Bin} ->
                  erlsom_lib:toUnicode(Bin);
                Error ->
                  Error
              end,
      %% io:format("parsing xml ...\n"),
      case lists:member('input', Options) of true -> debug(Input); _ -> true end,
      case lists:member('debug', Options) of true -> put(erlsom_debug, true); _ -> put(erlsom_debug, false) end,
      ParsedXml = erlsom:parse(Input, Model),
      erase(erlsom_debug),
      case lists:member('struct', Options) of true -> debug(ParsedXml); _ -> true end,
      case ParsedXml of
        {error, Message} ->
          case Result of
            "OK" -> 
              io:format("test case: ~p - ~p\n", [Name, Desc]),
              io:format("----> error: ~p\n", [Message]);
            _ -> true
          end;
        {ok, ParsingResult} ->
          case Result of
            "OK" -> true;
            _ -> 
              io:format("test case: ~p - ~p\n", [Name, Desc]),
              io:format("----> OK, but expected and error!\n")
          end,
          {ok, XmlOut} = erlsom:write(ParsingResult, Model),
          case lists:member('xml', Options) of true -> debug(XmlOut); _ -> true end,
          file:write_file(Xml ++ ".out.xml", 
                          ucs:to_utf8(XmlOut)),
          erlsom:write_hrl(XsdFile, Prefix, Namespaces, XsdFile ++ ".hrl")
      end
  end.

translateNS(undefined) ->
  [];
translateNS(NS) ->
  translateNS(NS, []).


translateNS([], Acc) ->
  Acc;
translateNS([#namespace{uri = Uri, prefix = Prefix}| Tail], Acc) ->
  translateNS(Tail, [#ns{uri = Uri, prefix = Prefix} | Acc]).

  

findCase([], Name) ->
  false;
findCase([Case = #'case'{name=Name}| _], Name) ->
  {ok, Case};
findCase([_| Tail], Name) ->
  findCase(Tail, Name).

test_soap(Test) ->
  case Test of
    "soaperror" ->
      debug("soap error"),
      FileName =  "tfcsoap-native.xsd",
      XMLFileName =  "error.xml",
      Prefix="b", 
      Uri = "http://www.carus.de/projects/xaction/schemas/2001/08/tfc/tfcsoap-native";
    "simpleSoap" ->
      debug("simple soap call"),
      FileName =  "stockprice.xsd",
      XMLFileName =  "stockprice.xml",
      Prefix="b", 
      Uri = "http://www.example.org/stock"
  end,
  XsdFile = "c:/werk/test-xsd/" ++ Test ++ "/" ++ FileName,
  {ok, Model} = erlsom:compile_file(XsdFile, {Prefix, Uri}),
  XmlFile = "c:/werk/test-xsd/" ++ Test ++ "/" ++ XMLFileName,
  erlsom:parseSoap_file(XmlFile, [{Uri, Model}]).

perf_test(Test, Iterations) ->
  ConfigXsd = "testConfig.xsd",
  {ok, ConfigModel} = erlsom:compile_file(ConfigXsd, []),
  ConfigXml = "testConfig.xml",
  {ok, TestSuite} = erlsom:parse_file(ConfigXml, ConfigModel),
  %% find test
  Cases = TestSuite#testConfig.cases,
  Dir = TestSuite#testConfig.dir,
  case findCase(Cases, Test) of
    {ok, #'case'{name=Name, desc=Desc, result=Result, xsd=XSD, xml=XML, namespaces=NS}} ->
      io:format("test case: ~p\n", [Name]),
      io:format("description: ~p\n", [Desc]),
      XsdFile = filename:join([Dir, Name, XSD]),
      Namespaces = case NS of undefined -> []; _ -> NS end,
      {ok, Model} = erlsom:compile_file(XsdFile, Namespaces),
      XmlFile = filename:join([Dir, Name, XML]),
      case file:read_file(XmlFile) of
        {ok, Bin} ->
          timer:tc(test_erlsom, parse_n_times, [binary_to_list(Bin), Model, Iterations]);
        Error ->
          Error
      end;
    _Else -> debug("Case not found")
  end.

perf_test_xmerl(Test, Iterations) ->
  ConfigXsd = "testConfig.xsd",
  {ok, ConfigModel} = erlsom:compile_file(ConfigXsd, []),
  ConfigXml = "testConfig.xml",
  {ok, TestSuite} = erlsom:parse_file(ConfigXml, ConfigModel),
  %% find test
  Cases = TestSuite#testConfig.cases,
  Dir = TestSuite#testConfig.dir,
  case findCase(Cases, Test) of
    {ok, #'case'{name=Name, desc=Desc, result=Result, xsd=XSD, xml=XML, namespaces=NS}} ->
      io:format("test case: ~p\n", [Name]),
      io:format("description: ~p\n", [Desc]),
      XmlFile = filename:join([Dir, Name, XML]),
      case file:read_file(XmlFile) of
        {ok, Bin} ->
          timer:tc(test_erlsom, parse_n_times_xmerl, [binary_to_list(Bin), Iterations]);
        Error ->
          Error
      end;
    _Else -> debug("Case not found")
  end.

parse_n_times(Xml, Model, 1) ->
   erlsom:parse(Xml, Model);
parse_n_times(Xml, Model, N) ->
   erlsom:parse(Xml, Model),
   parse_n_times(Xml, Model, N - 1).
   
parse_n_times_xmerl(Xml, 1) ->
   xmerl_scan:string(Xml);
parse_n_times_xmerl(Xml, N) ->
   xmerl_scan:string(Xml),
   parse_n_times_xmerl(Xml, N - 1).
   
